/*+***********************************************************************************
 Filename: 9k_mcu01_mycore_v01\src\top.v
 Description: a simple MCU with rom, ram and peripheral. without interruption.

 Modification:
   2025.08.16 Creation   H.Zheng
              porting from previous design.

Copyright (C) 2024-2025  Zheng Hui (hzheng@gzhu.edu.cn)

License: MulanPSL-2.0

***********************************************************************************-*/

module top (
  input wire [1:0] button,
  input wire sys_clk,
  output wire [5:0] led,
  input wire uart0_rxd,
  output wire uart0_txd
);
  localparam ROM_SIZE_IN_KB = 8;
  localparam RAM_SIZE_IN_KB = 16;

  //reset signals
  wire reset_n = button[1];

  //PLL to generate clk for ROM and RAM
  wire clk_192MHz, clk_32MHz;
  Gowin_rPLL_192M_32M mpll(
    .clkout(clk_192MHz), //output clkout
    .clkoutd(clk_32MHz), //output clkoutd
    .clkin(sys_clk) //input clkin
  );

  reg [1:0] clk_counter;
  always @(posedge clk_32MHz) begin
    clk_counter <= clk_counter + 1'b1;
  end  

//  wire core_clk = clk_counter[1];
  wire core_clk = clk_32MHz;



  //core
  wire [31:0] ibus_addr;
  wire [31:0] instruction;
  wire [31:0] monitor_port;
  wire [31:0] core_loadstore_addr;
  wire [31:0] core_loadstore_data_in;
  wire [31:0] core_loadstore_data_out;
  wire core_loadstore_ce;
  wire core_loadstore_we;
  wire [3:0] core_loadstore_wmask;

  zh_core_v01 m_core(
//    .clk(sys_clk),
    .clk(core_clk),
    .rst_n(reset_n),

    .ibus_addr(ibus_addr),
    .instruction_i(instruction),

    .core_loadstore_addr(core_loadstore_addr),
    .core_loadstore_data_in(core_loadstore_data_in),
    .core_loadstore_data_out(core_loadstore_data_out),
    .core_loadstore_ce(core_loadstore_ce),
    .core_loadstore_we(core_loadstore_we),
    .core_loadstore_wmask(core_loadstore_wmask),

    .monitor_port(monitor_port)
 );

  //load/store bus multiplexer
  wire mux_d0_en = core_loadstore_ce & (core_loadstore_addr[31:28] == 4'b0000);
  wire mux_d1_en = core_loadstore_ce & (core_loadstore_addr[31:28] == 4'b0001);
  wire mux_d2_en = core_loadstore_ce & (core_loadstore_addr[31:28] == 4'b0010);

  wire[31:0] mux_d0_data_i, mux_d1_data_i, mux_d2_data_i;
  wire[31:0] mux_d0_addr_o, mux_d1_addr_o, mux_d2_addr_o;
  wire[31:0] mux_d1_data_o, mux_d2_data_o;

  assign core_loadstore_data_in = mux_d0_en ? mux_d0_data_i :
                     mux_d1_en ? mux_d1_data_i :
                     mux_d2_en ? mux_d2_data_i : 32'b0;
  assign mux_d0_addr_o = (mux_d0_en) ? core_loadstore_addr : 32'b0;
  assign mux_d1_addr_o = (mux_d1_en) ? core_loadstore_addr : 32'b0;
  assign mux_d2_addr_o = (mux_d2_en) ? core_loadstore_addr : 32'b0;
  assign mux_d1_data_o = (mux_d1_en) ? core_loadstore_data_out : 32'b0;
  assign mux_d2_data_o = (mux_d2_en) ? core_loadstore_data_out : 32'b0;



  //rom
  zh_dprom_v01 #(.ROM_SIZE_IN_KB(ROM_SIZE_IN_KB)) I_ROM(
    .clk(clk_192MHz),
//    .ce(mux_d0_en),
    .addr_a(ibus_addr[clogb2(ROM_SIZE_IN_KB*256-1)+1:2]), 
    .dout_a(instruction),
    .addr_b(mux_d0_addr_o[clogb2(ROM_SIZE_IN_KB*256-1)+1:2]), 
    .dout_b(mux_d0_data_i)
  );

  //sram
  zh_sram_v01 #(.RAM_SIZE_IN_KB(RAM_SIZE_IN_KB)) D_RAM(
    .clk(clk_192MHz),
    .ce(mux_d2_en),
    .addr(mux_d2_addr_o[clogb2(RAM_SIZE_IN_KB*256-1)+1:2]), 
    .dout(mux_d2_data_i),
    .wclk(core_clk),
    .wen(core_loadstore_we),
    .wmask(core_loadstore_wmask),
    .din(mux_d2_data_o)
  );

  //peripherals
  wire [5:0] peri_led;
  wire peri_txd;    

  zh_peripheral_v01 #(.CLK_FREQ_IN_MHz(32)) m_peripheral(
//  zh_peripheral_v01 #(.CLK_FREQ_IN_MHz(8)) m_peripheral(
//      .clk(sys_clk),
      .clk(core_clk),
      .reset_n(reset_n),
      .button({2'b11,button[0]}), //TangNano9k has only two buttons, one for reset_n
      .led(peri_led),
      .rxd(uart0_rxd),
      .txd(uart0_txd),
      //load/store bus
      .ce(mux_d1_en),
      .wre(core_loadstore_we),
      .addr(mux_d1_addr_o[15:2]),
      .data_in(mux_d1_data_o),
      .data_out(mux_d1_data_i)
  );

  //output
  assign led = peri_led;
//  assign led = ~monitor_port[29:24];
//  assign led = ~monitor_port[5:0];


  //
  function integer clogb2;
    input integer depth;
      for (clogb2=0; depth>0; clogb2=clogb2+1)
        depth = depth >> 1;
  endfunction


endmodule